import os
import sqlite3

from wx.lib.pubsub import Publisher as pub

from .experiment import build_experiment_tree_info
from .utils import ShoppingCart

__all__ = ['Model']

class SearchDatabase():
    def __init__(self, dbpath):
        self._dbpath = dbpath
        self._dbcon = sqlite3.connect(self._dbpath)
        self._dbcur = self._dbcon.cursor()

    @property
    def good_searches(self):
        self._dbcur.execute("SELECT search_string FROM good_searches")
        return [row[0] for row in self._dbcur]

    def get_search_strings(self, bud):
        'Return a list of search strings which start with bud.'
        return_searches = []
        for search in self.good_searches:
            if search.startswith(bud):
                return_searches.append(search)
        return return_searches

    def submit_string(self, string, successful=False):
        '''Store submitted string in either the bad_searches table
            or the good_searches table based on successful.'''
        if successful:
            if string not in self.good_searches:
                self._dbcur.execute("""INSERT INTO good_searches (search_string)
                                      VALUES (?)""", (string,))
        else:
            self._dbcur.execute("""INSERT INTO bad_searches (search_string)
                                  VALUES (?)""", (string,))


class Model():
    'This presents the API for anything the program can do.'
    def __init__(self, search_dbpath):
        self.cart = ShoppingCart()

        self._dbcurs  = {} 
        self._dbcons  = {} 
        self._dbpaths = {}

        self.search_db = SearchDatabase(search_dbpath)

        self.experiments = {}
        self.experiment_tree = {}

    def search(self, dbid, search_string):
        """Execute the SQL statement in search_string on the
            database with dbid given.  If successful, add
            search string to the search_db.
        returns a sorted list of unique expe_ids from the search"""
        success = False
        try:
            self._dbcurs[dbid].execute(search_string)
            success = True
        except sqlite3.OperationalError as oe:
            pub.sendMessage("SEARCH FAILED", str(oe))
        self.search_db.submit_string(search_string, successful=success)
        expe_ids = [row[0] for row in self._dbcurs[dbid]]
        return sorted(list(set(expe_ids)))

    def _set_experiments(self, dbid):
        """Inputs:
            dbid    : database id
        Alters:
            self.experiments[dbid]
            self.experiment_tree[dbid]

        self.experiments[dbid]:
            A dictionary keyed on expe_ids with values of Experiments.
        self.experiment_tree[dbid]:
            A dictionary keyed first on spec_ids, then on sess_ids 
            and finally with values consisting of a list of Experiments
            having the spec_id and sess_id given.
        Note: No duplication of information occurs, as the experiment_tree
            is built from the same Experiments as are in the experiments
            dictionary."""
        results = build_experiment_tree_info(self._dbcurs[dbid], 
                                               self._dbpaths[dbid], 
                                               dbid)
        self.experiments[dbid], self.experiment_tree[dbid] = results
        pub.sendMessage(topic="SET EXPERIMENTS", data=dbid)

    def open_database(self, dbpath):
        # quietly fail if dbpath is None or not a real path.
        if dbpath is None:
            return
        elif not os.path.exists(dbpath):
            return

        "Try to open the database given the full path."
        for opened_dbid, opened_dbpath in self._dbpaths.items():
            if dbpath == opened_dbpath:
                pub.sendMessage("DATABASE ALREADY OPENED", data=opened_dbid)
                return
        try:
            dbcon = sqlite3.connect(dbpath)
        except:
            raise RuntimeError("Couldn't connect to the database %s"
                               % (dbpath) )
        try:
            dbcur = dbcon.cursor()
        except:
            raise RuntimeError("Couldn't create cursor for database %s"
                               % (db_fullpath) )
        with open(dbpath[:-15] + "database.id",'r') as dbid_file:
            dbid = dbid_file.readlines()[0][:-1] # don't keep eof or \n

        self._dbcurs[dbid] = dbcur
        self._dbcons[dbid] = dbcon
        self._dbpaths[dbid] = dbpath
        self._set_experiments(dbid)
        pub.sendMessage(topic="DATABASE OPENED", data=dbid)
